package com.datagateway.component;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 脚本版本管理器
 * 负责Groovy脚本的版本管理、A/B测试和脚本生命周期管理
 * 
 * @author Data Gateway Team
 * @version 1.0.0
 */
@Component
public class ScriptVersionManager {

    private static final Logger logger = LoggerFactory.getLogger(ScriptVersionManager.class);

    @Autowired
    private AlertManager alertManager;

    @Autowired
    private SystemMonitor systemMonitor;

    /**
     * 脚本版本统计
     */
    private final AtomicLong totalScriptVersions = new AtomicLong(0);
    private final AtomicLong totalScriptDeployments = new AtomicLong(0);
    private final AtomicLong totalScriptRollbacks = new AtomicLong(0);
    private final AtomicLong totalABTests = new AtomicLong(0);

    /**
     * 脚本版本配置
     */
    private String scriptRepositoryPath = "scripts/repository";
    private boolean versionControlEnabled = true;
    private int maxVersionsPerScript = 10;
    private int scriptValidationTimeoutSeconds = 30;
    private boolean autoBackupEnabled = true;
    private String backupPath = "scripts/backup";

    /**
     * 脚本版本存储
     */
    private final Map<String, List<ScriptVersion>> scriptVersions = new ConcurrentHashMap<>();
    private final Map<String, ScriptVersion> activeVersions = new ConcurrentHashMap<>();
    private final Map<String, ABTestConfiguration> abTestConfigs = new ConcurrentHashMap<>();

    /**
     * 执行器
     */
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2, r -> {
        Thread t = new Thread(r, "script-version-manager");
        t.setDaemon(true);
        return t;
    });

    /**
     * 初始化脚本版本管理器
     */
    public void initialize() {
        try {
            // 创建脚本仓库目录
            createScriptRepository();
            
            // 加载现有脚本版本
            loadExistingScriptVersions();
            
            // 启动脚本监控任务
            startScriptMonitoringTask();
            
            logger.info("脚本版本管理器初始化成功");
            
        } catch (Exception e) {
            logger.error("脚本版本管理器初始化失败", e);
            alertManager.sendSystemErrorAlert("脚本版本管理器初始化失败", e.getMessage());
        }
    }

    /**
     * 停止脚本版本管理器
     */
    public void stop() {
        try {
            scheduler.shutdown();
            if (!scheduler.awaitTermination(30, TimeUnit.SECONDS)) {
                scheduler.shutdownNow();
            }
            
            logger.info("脚本版本管理器停止成功");
            
        } catch (Exception e) {
            logger.error("脚本版本管理器停止失败", e);
        }
    }

    /**
     * 创建新脚本版本
     * 
     * @param scriptName 脚本名称
     * @param scriptContent 脚本内容
     * @param version 版本号
     * @param description 版本描述
     * @return 是否创建成功
     */
    public boolean createScriptVersion(String scriptName, String scriptContent, String version, String description) {
        if (!versionControlEnabled) {
            logger.debug("脚本版本控制已禁用，跳过版本创建: {}", scriptName);
            return false;
        }

        try {
            // 验证脚本内容
            if (!validateScriptContent(scriptContent)) {
                logger.error("脚本内容验证失败: {}", scriptName);
                return false;
            }

            // 创建脚本版本
            ScriptVersion scriptVersion = new ScriptVersion(
                scriptName,
                version,
                scriptContent,
                description,
                System.currentTimeMillis(),
                ScriptStatus.ACTIVE
            );

            // 保存到版本列表
            scriptVersions.computeIfAbsent(scriptName, k -> new ArrayList<>()).add(scriptVersion);
            
            // 保存到文件系统
            saveScriptVersionToFile(scriptVersion);
            
            // 如果是第一个版本，设置为活跃版本
            if (activeVersions.get(scriptName) == null) {
                activeVersions.put(scriptName, scriptVersion);
            }
            
            totalScriptVersions.incrementAndGet();
            
            logger.info("脚本版本创建成功: {} v{}", scriptName, version);
            
            return true;
            
        } catch (Exception e) {
            logger.error("创建脚本版本失败: {} v{}", scriptName, version, e);
            alertManager.sendSystemErrorAlert("创建脚本版本失败", e.getMessage());
            return false;
        }
    }

    /**
     * 部署脚本版本
     * 
     * @param scriptName 脚本名称
     * @param version 版本号
     * @return 是否部署成功
     */
    public boolean deployScriptVersion(String scriptName, String version) {
        try {
            ScriptVersion scriptVersion = getScriptVersion(scriptName, version);
            if (scriptVersion == null) {
                logger.error("脚本版本不存在: {} v{}", scriptName, version);
                return false;
            }

            // 备份当前活跃版本
            if (autoBackupEnabled && activeVersions.containsKey(scriptName)) {
                backupActiveVersion(scriptName);
            }

            // 设置新版本为活跃版本
            activeVersions.put(scriptName, scriptVersion);
            scriptVersion.setStatus(ScriptStatus.ACTIVE);
            scriptVersion.setDeploymentTime(System.currentTimeMillis());
            
            totalScriptDeployments.incrementAndGet();
            
            logger.info("脚本版本部署成功: {} v{}", scriptName, version);
            
            return true;
            
        } catch (Exception e) {
            logger.error("部署脚本版本失败: {} v{}", scriptName, version, e);
            return false;
        }
    }

    /**
     * 回滚脚本版本
     * 
     * @param scriptName 脚本名称
     * @param version 版本号
     * @return 是否回滚成功
     */
    public boolean rollbackScriptVersion(String scriptName, String version) {
        try {
            ScriptVersion scriptVersion = getScriptVersion(scriptName, version);
            if (scriptVersion == null) {
                logger.error("脚本版本不存在: {} v{}", scriptName, version);
                return false;
            }

            // 设置指定版本为活跃版本
            activeVersions.put(scriptName, scriptVersion);
            scriptVersion.setStatus(ScriptStatus.ACTIVE);
            scriptVersion.setRollbackTime(System.currentTimeMillis());
            
            totalScriptRollbacks.incrementAndGet();
            
            logger.info("脚本版本回滚成功: {} v{}", scriptName, version);
            
            return true;
            
        } catch (Exception e) {
            logger.error("回滚脚本版本失败: {} v{}", scriptName, version, e);
            return false;
        }
    }

    /**
     * 获取活跃脚本版本
     * 
     * @param scriptName 脚本名称
     * @return 活跃脚本版本
     */
    public ScriptVersion getActiveScriptVersion(String scriptName) {
        return activeVersions.get(scriptName);
    }

    /**
     * 获取脚本版本
     * 
     * @param scriptName 脚本名称
     * @param version 版本号
     * @return 脚本版本
     */
    public ScriptVersion getScriptVersion(String scriptName, String version) {
        List<ScriptVersion> versions = scriptVersions.get(scriptName);
        if (versions != null) {
            return versions.stream()
                .filter(v -> version.equals(v.getVersion()))
                .findFirst()
                .orElse(null);
        }
        return null;
    }

    /**
     * 获取脚本所有版本
     * 
     * @param scriptName 脚本名称
     * @return 版本列表
     */
    public List<ScriptVersion> getScriptVersions(String scriptName) {
        return scriptVersions.getOrDefault(scriptName, new ArrayList<>());
    }

    /**
     * 配置A/B测试
     * 
     * @param scriptName 脚本名称
     * @param versionA 版本A
     * @param versionB 版本B
     * @param trafficRatio 流量比例（0.0-1.0）
     * @param duration 测试持续时间（分钟）
     * @return 是否配置成功
     */
    public boolean configureABTest(String scriptName, String versionA, String versionB, 
                                 double trafficRatio, int duration) {
        try {
            // 验证版本存在
            ScriptVersion versionAObj = getScriptVersion(scriptName, versionA);
            ScriptVersion versionBObj = getScriptVersion(scriptName, versionB);
            
            if (versionAObj == null || versionBObj == null) {
                logger.error("A/B测试版本不存在: {} v{} vs v{}", scriptName, versionA, versionB);
                return false;
            }

            // 创建A/B测试配置
            ABTestConfiguration abTestConfig = new ABTestConfiguration(
                scriptName,
                versionA,
                versionB,
                trafficRatio,
                duration,
                System.currentTimeMillis()
            );

            abTestConfigs.put(scriptName, abTestConfig);
            totalABTests.incrementAndGet();
            
            logger.info("A/B测试配置成功: {} v{} vs v{} (比例: {})", 
                       scriptName, versionA, versionB, trafficRatio);
            
            return true;
            
        } catch (Exception e) {
            logger.error("配置A/B测试失败: {} v{} vs v{}", scriptName, versionA, versionB, e);
            return false;
        }
    }

    /**
     * 获取A/B测试配置
     * 
     * @param scriptName 脚本名称
     * @return A/B测试配置
     */
    public ABTestConfiguration getABTestConfiguration(String scriptName) {
        return abTestConfigs.get(scriptName);
    }

    /**
     * 停止A/B测试
     * 
     * @param scriptName 脚本名称
     * @param winningVersion 获胜版本
     * @return 是否停止成功
     */
    public boolean stopABTest(String scriptName, String winningVersion) {
        try {
            ABTestConfiguration abTestConfig = abTestConfigs.remove(scriptName);
            if (abTestConfig == null) {
                logger.warn("A/B测试配置不存在: {}", scriptName);
                return false;
            }

            // 部署获胜版本
            if (winningVersion != null) {
                deployScriptVersion(scriptName, winningVersion);
            }

            logger.info("A/B测试停止成功: {} 获胜版本: {}", scriptName, winningVersion);
            
            return true;
            
        } catch (Exception e) {
            logger.error("停止A/B测试失败: {}", scriptName, e);
            return false;
        }
    }

    /**
     * 获取脚本版本统计信息
     * 
     * @return 统计信息
     */
    public ScriptVersionStatistics getStatistics() {
        return new ScriptVersionStatistics(
            totalScriptVersions.get(),
            totalScriptDeployments.get(),
            totalScriptRollbacks.get(),
            totalABTests.get(),
            scriptVersions.size(),
            activeVersions.size(),
            abTestConfigs.size(),
            versionControlEnabled,
            maxVersionsPerScript,
            scriptValidationTimeoutSeconds,
            System.currentTimeMillis()
        );
    }

    /**
     * 设置脚本版本配置
     * 
     * @param enabled 是否启用版本控制
     * @param repositoryPath 仓库路径
     * @param maxVersions 最大版本数
     * @param validationTimeout 验证超时时间
     * @param autoBackup 自动备份
     * @param backupPath 备份路径
     */
    public void setConfiguration(boolean enabled, String repositoryPath, int maxVersions,
                               int validationTimeout, boolean autoBackup, String backupPath) {
        this.versionControlEnabled = enabled;
        this.scriptRepositoryPath = repositoryPath;
        this.maxVersionsPerScript = maxVersions;
        this.scriptValidationTimeoutSeconds = validationTimeout;
        this.autoBackupEnabled = autoBackup;
        this.backupPath = backupPath;
        
        // 创建目录
        createScriptRepository();
        
        logger.info("脚本版本配置已更新: 启用={}, 仓库路径={}, 最大版本={}, 验证超时={}, 自动备份={}, 备份路径={}", 
                   enabled, repositoryPath, maxVersions, validationTimeout, autoBackup, backupPath);
    }

    /**
     * 验证脚本内容
     */
    private boolean validateScriptContent(String scriptContent) {
        try {
            // 这里可以实现具体的脚本验证逻辑
            // 例如：语法检查、安全性检查等
            // 简化实现：检查脚本内容不为空
            return scriptContent != null && !scriptContent.trim().isEmpty();
            
        } catch (Exception e) {
            logger.error("脚本内容验证失败", e);
            return false;
        }
    }

    /**
     * 保存脚本版本到文件
     */
    private void saveScriptVersionToFile(ScriptVersion scriptVersion) throws IOException {
        String fileName = scriptVersion.getScriptName() + "_" + scriptVersion.getVersion() + ".groovy";
        Path filePath = Paths.get(scriptRepositoryPath, fileName);
        
        Files.createDirectories(filePath.getParent());
        Files.write(filePath, scriptVersion.getContent().getBytes());
    }

    /**
     * 备份活跃版本
     */
    private void backupActiveVersion(String scriptName) {
        try {
            ScriptVersion activeVersion = activeVersions.get(scriptName);
            if (activeVersion != null) {
                String backupFileName = scriptName + "_backup_" + System.currentTimeMillis() + ".groovy";
                Path backupFilePath = Paths.get(backupPath, backupFileName);
                
                Files.createDirectories(backupFilePath.getParent());
                Files.write(backupFilePath, activeVersion.getContent().getBytes());
                
                logger.debug("活跃版本备份成功: {}", scriptName);
            }
        } catch (Exception e) {
            logger.error("备份活跃版本失败: {}", scriptName, e);
        }
    }

    /**
     * 创建脚本仓库
     */
    private void createScriptRepository() {
        try {
            Files.createDirectories(Paths.get(scriptRepositoryPath));
            if (autoBackupEnabled) {
                Files.createDirectories(Paths.get(backupPath));
            }
        } catch (Exception e) {
            logger.error("创建脚本仓库失败", e);
        }
    }

    /**
     * 加载现有脚本版本
     */
    private void loadExistingScriptVersions() {
        try {
            Path repoPath = Paths.get(scriptRepositoryPath);
            if (Files.exists(repoPath)) {
                Files.walk(repoPath)
                    .filter(Files::isRegularFile)
                    .filter(path -> path.toString().endsWith(".groovy"))
                    .forEach(this::loadScriptVersionFromFile);
            }
        } catch (Exception e) {
            logger.error("加载现有脚本版本失败", e);
        }
    }

    /**
     * 从文件加载脚本版本
     */
    private void loadScriptVersionFromFile(Path filePath) {
        try {
            String fileName = filePath.getFileName().toString();
            String[] parts = fileName.replace(".groovy", "").split("_");
            
            if (parts.length >= 2) {
                String scriptName = parts[0];
                String version = parts[1];
                String content = new String(Files.readAllBytes(filePath));
                
                ScriptVersion scriptVersion = new ScriptVersion(
                    scriptName,
                    version,
                    content,
                    "从文件加载",
                    Files.getLastModifiedTime(filePath).toMillis(),
                    ScriptStatus.INACTIVE
                );
                
                scriptVersions.computeIfAbsent(scriptName, k -> new ArrayList<>()).add(scriptVersion);
            }
        } catch (Exception e) {
            logger.error("从文件加载脚本版本失败: {}", filePath, e);
        }
    }

    /**
     * 启动脚本监控任务
     */
    private void startScriptMonitoringTask() {
        scheduler.scheduleAtFixedRate(this::monitorScriptVersions, 
            0, 60, TimeUnit.SECONDS);
    }

    /**
     * 监控脚本版本
     */
    private void monitorScriptVersions() {
        try {
            // 清理过期版本
            cleanupExpiredVersions();
            
            // 检查A/B测试状态
            checkABTestStatus();
            
        } catch (Exception e) {
            logger.error("监控脚本版本失败", e);
        }
    }

    /**
     * 清理过期版本
     */
    private void cleanupExpiredVersions() {
        try {
            for (Map.Entry<String, List<ScriptVersion>> entry : scriptVersions.entrySet()) {
                List<ScriptVersion> versions = entry.getValue();
                if (versions.size() > maxVersionsPerScript) {
                    // 按创建时间排序，保留最新的版本
                    versions.sort(Comparator.comparing(ScriptVersion::getCreateTime).reversed());
                    
                    List<ScriptVersion> toRemove = versions.subList(maxVersionsPerScript, versions.size());
                    versions.removeAll(toRemove);
                    
                    logger.info("清理过期脚本版本: {} 个版本", toRemove.size());
                }
            }
        } catch (Exception e) {
            logger.error("清理过期版本失败", e);
        }
    }

    /**
     * 检查A/B测试状态
     */
    private void checkABTestStatus() {
        try {
            long currentTime = System.currentTimeMillis();
            List<String> expiredTests = new ArrayList<>();
            
            for (Map.Entry<String, ABTestConfiguration> entry : abTestConfigs.entrySet()) {
                ABTestConfiguration config = entry.getValue();
                if (currentTime - config.getStartTime() > config.getDuration() * 60 * 1000) {
                    expiredTests.add(entry.getKey());
                }
            }
            
            for (String scriptName : expiredTests) {
                logger.info("A/B测试已过期: {}", scriptName);
                // 这里可以自动停止过期的A/B测试
            }
        } catch (Exception e) {
            logger.error("检查A/B测试状态失败", e);
        }
    }

    /**
     * 脚本版本类
     */
    public static class ScriptVersion {
        private final String scriptName;
        private final String version;
        private final String content;
        private final String description;
        private final long createTime;
        private ScriptStatus status;
        private long deploymentTime;
        private long rollbackTime;

        public ScriptVersion(String scriptName, String version, String content, 
                           String description, long createTime, ScriptStatus status) {
            this.scriptName = scriptName;
            this.version = version;
            this.content = content;
            this.description = description;
            this.createTime = createTime;
            this.status = status;
        }

        // Getter和Setter方法
        public String getScriptName() { return scriptName; }
        public String getVersion() { return version; }
        public String getContent() { return content; }
        public String getDescription() { return description; }
        public long getCreateTime() { return createTime; }
        public ScriptStatus getStatus() { return status; }
        public void setStatus(ScriptStatus status) { this.status = status; }
        public long getDeploymentTime() { return deploymentTime; }
        public void setDeploymentTime(long deploymentTime) { this.deploymentTime = deploymentTime; }
        public long getRollbackTime() { return rollbackTime; }
        public void setRollbackTime(long rollbackTime) { this.rollbackTime = rollbackTime; }
    }

    /**
     * 脚本状态枚举
     */
    public enum ScriptStatus {
        ACTIVE,     // 活跃
        INACTIVE,   // 非活跃
        DEPRECATED  // 已废弃
    }

    /**
     * A/B测试配置类
     */
    public static class ABTestConfiguration {
        private final String scriptName;
        private final String versionA;
        private final String versionB;
        private final double trafficRatio;
        private final int duration;
        private final long startTime;

        public ABTestConfiguration(String scriptName, String versionA, String versionB,
                                 double trafficRatio, int duration, long startTime) {
            this.scriptName = scriptName;
            this.versionA = versionA;
            this.versionB = versionB;
            this.trafficRatio = trafficRatio;
            this.duration = duration;
            this.startTime = startTime;
        }

        // Getter方法
        public String getScriptName() { return scriptName; }
        public String getVersionA() { return versionA; }
        public String getVersionB() { return versionB; }
        public double getTrafficRatio() { return trafficRatio; }
        public int getDuration() { return duration; }
        public long getStartTime() { return startTime; }
    }

    /**
     * 脚本版本统计信息类
     */
    public static class ScriptVersionStatistics {
        private final long totalScriptVersions;
        private final long totalScriptDeployments;
        private final long totalScriptRollbacks;
        private final long totalABTests;
        private final int totalScripts;
        private final int activeScripts;
        private final int activeABTests;
        private final boolean versionControlEnabled;
        private final int maxVersionsPerScript;
        private final int scriptValidationTimeout;
        private final long timestamp;

        public ScriptVersionStatistics(long totalScriptVersions, long totalScriptDeployments,
                                     long totalScriptRollbacks, long totalABTests,
                                     int totalScripts, int activeScripts, int activeABTests,
                                     boolean versionControlEnabled, int maxVersionsPerScript,
                                     int scriptValidationTimeout, long timestamp) {
            this.totalScriptVersions = totalScriptVersions;
            this.totalScriptDeployments = totalScriptDeployments;
            this.totalScriptRollbacks = totalScriptRollbacks;
            this.totalABTests = totalABTests;
            this.totalScripts = totalScripts;
            this.activeScripts = activeScripts;
            this.activeABTests = activeABTests;
            this.versionControlEnabled = versionControlEnabled;
            this.maxVersionsPerScript = maxVersionsPerScript;
            this.scriptValidationTimeout = scriptValidationTimeout;
            this.timestamp = timestamp;
        }

        // Getter方法
        public long getTotalScriptVersions() { return totalScriptVersions; }
        public long getTotalScriptDeployments() { return totalScriptDeployments; }
        public long getTotalScriptRollbacks() { return totalScriptRollbacks; }
        public long getTotalABTests() { return totalABTests; }
        public int getTotalScripts() { return totalScripts; }
        public int getActiveScripts() { return activeScripts; }
        public int getActiveABTests() { return activeABTests; }
        public boolean isVersionControlEnabled() { return versionControlEnabled; }
        public int getMaxVersionsPerScript() { return maxVersionsPerScript; }
        public int getScriptValidationTimeout() { return scriptValidationTimeout; }
        public long getTimestamp() { return timestamp; }
    }
}
